(defpackage :regrin-neural-library
  (:use :common-lisp)
  (:nicknames :fnl)
  (:export :forward 
           :out-layer-error
	       :backward
           :weights-correction
	       :relu
           :relu-deriv
           :sigmoid
           :sigmoid-deriv
	       :sign
           :leaky-relu
	       :leaky-relu-deriv
	       :random-weights))
	   
(in-package :regrin-neural-library)
  

;Данная библиотека предназначена для создания простых нейронных сетей
;Сигналы на входах представлены в виде одномерных списков длины n: (I1 I2 ... In)
;Веса записываюся в виде двумерных списков n * m:
                    ;         ((W11 W12 ... W1n)
					;          (W21 W22 ... W2n)
					;                 ...
					;          (Wm1 Wm2 ... Wmn))
;Число выходных нейронов равно числу вложенных списков, а число входных - количеству элементов в них.
(defun scalar (a-list b-list)
    (loop for a in a-list
	      for b in b-list summing (* a b)))


;При прямом прохождении каждый входной сигнал умножается на соответствующий вес и их сумма проходит через  
;функцию активации. Её результат и будет выходным сигналом. Этот процесс повторяется для всех нейронов.
;Его можно представить как скалярное произведение списка входных сигналов на список весов: 
;((I1*W11 + I2*W12 + ... + In*W1n)
; (I1*W21 + I2*W22 + ... + In*W2n)
;               ...
; (I1*Wm1 + I2*Wm2 + ... + In*Wmn))
;
;В результате мы получаем список чисел, которые мы пропускаем через функцию активации и получаем список выходных сигналов
(defun forward (fun inp weights)
    (mapcar fun (loop for wght in weights
		              collect (scalar inp wght))))

(defun out-layer-error (ideal actual)
    (mapcar #'- ideal actual))


(defun number*list (num lst) 
    (loop for elm in lst collect (* elm num)))

(defun backward (errors weights)
    (apply #'mapcar #'+ (loop for wght in weights
			                  for err  in errors
			                  collect (number*list err wght))))

(defun weights-correction (deriv-fun learn-rate input-layer errors-list weights-matrix)
    (loop for error in errors-list
	      for weights-list in weights-matrix
	      collect (loop for inp in input-layer
		                for weight in weights-list
		                collect (+ weight (* error
					                         learn-rate
					                         (funcall deriv-fun inp))))))
(defun random-weights (num-of-inputs num-of-outputs)
    (loop for i upto (1- num-of-outputs)
	      collect (loop for j upto (1- num-of-inputs)
		                collect (/ (random 1000)
				                   1000.0))))

(defun relu (x)
    (if (< x 0) 0 x))

(defun relu-deriv (x)
    (if (< x 0)
        0
        1))

(defun sigmoid (x)
    (/ 1 (+ 1 (exp (- x)))))

(defun sigmoid-deriv (x)
    (* (sigmoid x)
       (- 1 (sigmoid x))))

(defun sign (x)
    (if (< x 0)
      0
      1))

(defun linum (x)
    x)
(defun linum-deriv (x)
    1)

(defun leaky-relu (x)
    (if (< x 0)
        (* x 0.01)
        x))
(defun leaky-relu-deriv (x)
    (if (< x 0)
        0.01
        1))
